﻿using Microsoft.Kinect;
using System;
using System.Collections.Generic;
using System.IO;
using System.Speech.Recognition;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;


namespace DicomViewerScreens
{
    public delegate void OnSpeechActionPerformed(String action, String Message);
	/// <summary>
	/// Interaction logic for VoicePanel.xaml
	/// </summary>
	public partial class VoicePanel : UserControl
    {
        double amplitude;
        #region event
        public EventHandler OnBeamAngleChange;
        #endregion
        #region local feilds
        /// <summary>
        /// Array of foreground-color pixels corresponding to a line as long as the energy bitmap is tall.
        /// This gets re-used while constructing the energy visualization.
        /// </summary>
        private byte[] foregroundPixels;

        /// <summary>
        /// Width of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapWidth = 780;

        /// <summary>
        /// Height of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapHeight = 195;

        /// <summary>
        /// Stream of audio being captured by Kinect sensor.
        /// </summary>
        private Stream audioStream;

        /// <summary>
        /// The WritableBitmap
        /// </summary>
        public WriteableBitmap EnergySource { get; set; }


        public KinectSensor sensor { set; get; }

        /// <summary>
        /// Thread that is reading audio from Kinect stream.
        /// </summary>
        private Thread readingThread;

        /// <summary>
        /// <code>true</code> if audio is currently being read from Kinect stream, <code>false</code> otherwise.
        /// </summary>
        private bool reading;


        /// <summary>
        /// Array of background-color pixels corresponding to an area equal to the size of whole energy bitmap.
        /// </summary>
        private readonly byte[] backgroundPixels = new byte[EnergyBitmapWidth * EnergyBitmapHeight];

        /// <summary>
        /// Buffer used to hold audio data read from audio stream.
        /// </summary>
        private readonly byte[] audioBuffer = new byte[AudioPollingInterval * SamplesPerMillisecond * BytesPerSample];

        /// <summary>
        /// Number of milliseconds between each read of audio data from the stream.
        /// </summary>
        private const int AudioPollingInterval = 50;

        /// <summary>
        /// Number of samples captured from Kinect audio stream each millisecond.
        /// </summary>
        private const int SamplesPerMillisecond = 16;

        /// <summary>
        /// Number of bytes in each Kinect audio stream sample.
        /// </summary>
        private const int BytesPerSample = 2;

        /// <summary>
        /// Sum of squares of audio samples being accumulated to compute the next energy value.
        /// </summary>
        private double accumulatedSquareSum;


        /// <summary>
        /// Number of audio samples accumulated so far to compute the next energy value.
        /// </summary>
        private int accumulatedSampleCount;


        /// <summary>
        /// Number of audio samples represented by each column of pixels in wave bitmap.
        /// </summary>
        private const int SamplesPerColumn = 40;


        /// <summary>
        /// Buffer used to store audio stream energy data as we read audio.
        /// 
        /// We store 25% more energy values than we strictly need for visualization to allow for a smoother
        /// stream animation effect, since rendering happens on a different schedule with respect to audio
        /// capture.
        /// </summary>
        private readonly double[] energy = new double[(uint)(EnergyBitmapWidth * 1.25)];



        /// <summary>
        /// Index of next element available in audio energy buffer.
        /// </summary>
        private int energyIndex;

        /// <summary>
        /// Number of newly calculated audio stream energy values that have not yet been
        /// displayed.
        /// </summary>
        private int newEnergyAvailable;

        #endregion
        delegate void UpdateMicIndecator(int max, int value);
        public VoicePanel()
		{
			this.InitializeComponent();
		}
        
        public void InitaliseKinectAudio()
        {

            // Initialize foreground pixels
            this.foregroundPixels = new byte[EnergyBitmapHeight];
            for (int i = 0; i < this.foregroundPixels.Length; ++i)
            {
                this.foregroundPixels[i] = 0xff;
            }

           // EnergySource = this.energyBitmap;

            // Start streaming audio!
            this.audioStream = this.sensor.AudioSource.Start();
            sensor.AudioSource.BeamAngleChanged += AudioSource_BeamAngleChanged;

            Thread th = new Thread(new System.Threading.ThreadStart(AudioReadingThread));
            th.Start();
        }
     
        void AudioSource_BeamAngleChanged(object sender, BeamAngleChangedEventArgs e)
        {
            Beamindecator.setIndecator((float)((e.Angle-KinectAudioSource.MinBeamAngle)/(KinectAudioSource.MaxBeamAngle-KinectAudioSource.MinBeamAngle)));
         }
     
        #region Audio Thread
        /// <summary>
        /// Handles polling audio stream and updating visualization every tick.
        /// </summary>
        private void AudioReadingThread()
        {

            // Bottom portion of computed energy signal that will be discarded as noise.
            // Only portion of signal above noise floor will be displayed.
            const double EnergyNoiseFloor = 0.2;
            {

                while (this.reading)
                {
                    int readCount = audioStream.Read(audioBuffer, 0, audioBuffer.Length);
                    
                    for (int i = 0; i < readCount; i += 2)
                    {
                        // compute the sum of squares of audio samples that will get accumulated
                        // into a single energy value.
                        short audioSample = BitConverter.ToInt16(audioBuffer, i);
                        this.accumulatedSquareSum += audioSample * audioSample;
                        ++this.accumulatedSampleCount;


                        // Each energy value will represent the logarithm of the mean of the
                        // sum of squares of a group of audio samples.
                        double meanSquare = this.accumulatedSquareSum / SamplesPerColumn;
                        amplitude = Math.Log(meanSquare) / Math.Log(int.MaxValue);


                        this.energy[this.energyIndex] = Math.Max(0, amplitude - EnergyNoiseFloor) / (1 - EnergyNoiseFloor);
                        this.energyIndex = (this.energyIndex + 1) % this.energy.Length;
                       // this.Dispatcher.Invoke(new Action(OnUpDateMicBackGround));
                        this.accumulatedSquareSum = 0;
                        this.accumulatedSampleCount = 0;
                        ++this.newEnergyAvailable;
                    }
                }

            }


        }

        void OnUpDateMicBackGround()
        {
            micbackground.Fill = new LinearGradientBrush(Colors.White, Colors.Aquamarine, new Point(0.5, 1), new Point(0.5, 1-amplitude));
           

        }
        #endregion
        internal void Shutdown()
        {
            this.reading = false;
            
        }
        private void VoicePannelLoaded(object sender, RoutedEventArgs e)
        {
            this.InitaliseKinectAudio();
        }
    }

   
    
}